package Server;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import static java.rmi.server.UnicastRemoteObject.unexportObject;
import java.sql.*;
import java.util.ArrayList;
import java.util.Enumeration;

import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
import rmip.ClientInt;

import java.util.Properties;
import java.util.Vector;

import rmip.ServerInt;
import javax.mail.*;
import javax.mail.internet.*;
import javax.swing.JOptionPane;

public class ServerImp extends UnicastRemoteObject implements ServerInt {

    Hashtable<String, ClientInt> online = new Hashtable<String, ClientInt>();
    Hashtable<String, String> status = new Hashtable<String, String>();
    Connection c;
    PreparedStatement ps;
    PreparedStatement ps2;
    PreparedStatement ps3;
    PreparedStatement ps4;
    ResultSet rs;
    String dbname;
    String dbuser;
    String dbpw;
    String tableName;
    ServerGUI gui;

    Registry r;

    public ServerImp() throws RemoteException {
        dbname = "chat";
        dbuser = "root";
        dbpw = "iti";
        tableName = "user";

        try {
            c = DriverManager.getConnection("jdbc:mysql://127.0.0.1/" + dbname + "?" + "user=" + dbuser + "&password=" + dbpw);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public String getNickName(String email) {

        String nick = null;
        try {
            ps2 = c.prepareStatement("select * from user where email=?");
            ps2.setString(1, email);
            rs = ps2.executeQuery();
            rs.next();
            nick = rs.getString(4);
        } catch (SQLException ex) {
            Logger.getLogger(ServerImp.class.getName()).log(Level.SEVERE, null, ex);
        }
        return nick;
    }

    public int getcount() {
        try {
            ps2 = c.prepareStatement("select count(*) from user ");
            rs = ps2.executeQuery();
            rs.next();
            return rs.getInt(1);
        } catch (SQLException ex) {
            Logger.getLogger(ServerImp.class.getName()).log(Level.SEVERE, null, ex);
        }
        return -1;

    }

    @Override
    public int getId(String email) {

        int id = 0;
        try {
            ps2 = c.prepareStatement("select * from user where email=?");
            ps2.setString(1, email);
            rs = ps2.executeQuery();
            rs.next();
            id = rs.getInt(1);
        } catch (SQLException ex) {
            Logger.getLogger(ServerImp.class.getName()).log(Level.SEVERE, null, ex);
        }
        return id;
    }

    @Override
    public boolean register(String email, String pw, String nn, char g, Date d, String pp) throws RemoteException {

        try {
            ps2 = c.prepareStatement("select email from user where email=?");

            ps2.setString(1, email);

            rs = ps2.executeQuery();

            if (rs.next()) {
                return false;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        try {
            ps = c.prepareStatement("insert into user (email, password, nickname, gender, dob, picture) values (?,?,?,?,?,?)");
            ps.setString(1, email);
            ps.setString(2, pw);
            ps.setString(3, nn);
            ps.setString(4, String.valueOf(g));
            ps.setDate(5, d);
            ps.setString(6, pp);

            int qstat = ps.executeUpdate();
            System.out.println("The result of the query: " + qstat);
        } catch (SQLException ex) {
            ex.printStackTrace();
            return false;
        }
        return true;
    }

    @Override
    public boolean login(String email, String pw) {
        System.out.println("login");

        try {

            ps2 = c.prepareStatement("select password from user where email=?");
            ps2.setString(1, email);

            rs = ps2.executeQuery();

            if (!rs.next()) {
                rs.beforeFirst();
                System.out.println("in if(!rs.next())");
                ps2 = c.prepareStatement("select email from user where email=?");
                ps2.setString(1, email);
                rs = ps2.executeQuery();

                if (rs.next()) {
                    System.out.println("checking if email is in db");
                    System.out.println("Email available, wrong password");

                    return false;
                } else {
                    System.out.println("Email not available");
                }
                rs.beforeFirst();
                return false;
            } else {
                System.out.println("in else of if(!rs.next())");
                String hashedpw = rs.getString("password");
                System.out.println("comparing pws");
                if (BCrypt.checkpw(pw, hashedpw)) {
                    System.out.println("in if checkpw cmp pw hashed from db");
                    System.out.println("MATCH");
                } else {
                    System.out.println("Wrong Password!");
                    return false;
                }
            }

            return true;
        } catch (SQLException ex) {
            ex.printStackTrace();
            return false;
        }
    }

    public void setStatus(String email, String s) {
        status.put(email, s);
        Hashtable<String, String> friends = new Hashtable();
        try {
            friends = getFriends(email);
            Enumeration<String> e = friends.keys();
            while (e.hasMoreElements()) {
                Object o = e.nextElement();
                if (online.containsKey(o)) {
                    online.get(o).repaintList();
                }
            }
        } catch (RemoteException ex) {
            Logger.getLogger(ServerImp.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void signUp(String email, ClientInt cli) throws RemoteException {
        online.put(email, cli);
        setStatus(email, "online");
        gui.setonline(online.size());

    }

    @Override
    public void signOut(String cli) throws RemoteException {
      setStatus(cli,"offline");  
      online.remove(cli);
        gui.setonline(online.size());
    }

    @Override
    public void sendMessageToServer(String msg) throws RemoteException {
        System.out.println(msg);
    }

    @Override
    public boolean sendMail(String to, String from, String subject, String msg) throws RemoteException {
        String host = "localhost";

        // Get system properties
        Properties properties = System.getProperties();

        // Setup mail server
        properties.setProperty("mail.smtp.host", host);

        // Get the default Session object.
        Session session = Session.getDefaultInstance(properties);

        try {
            // Create a default MimeMessage object.
            MimeMessage message = new MimeMessage(session);

            // Set From: header field of the header.
            message.setFrom(new InternetAddress(from));

            // Set To: header field of the header.
            message.addRecipient(Message.RecipientType.TO,
                    new InternetAddress(to));

            // Set Subject: header field
            message.setSubject(subject);

            // Now set the actual message
            message.setText(msg);

            // Send message
            Transport.send(message);
            System.out.println(to + from + subject + msg);
            return true;
        } catch (MessagingException mex) {
            mex.printStackTrace();
            return false;
        }

    }

    @Override
    public void sendmessagebymail(String sender, String reciver, String Message) throws RemoteException {

        ClientInt ch;
        if (online.containsKey(reciver)) {
            ch = online.get(reciver);
            ch.convGui(reciver, sender, this);
            ch.sendMessageToClient(sender, Message);

        } else {
            online.get(sender).sendMessage(reciver + "offline now");
        }

    }

    public void SendInerActMessage(String sender, String reciver, String Message) throws RemoteException {

        ClientInt ch;
        if (online.containsKey(reciver)) {
            ch = online.get(reciver);
            ch.convGui(reciver, sender, this);
            ch.sendGMessageToInterActArea(sender, Message);

        } else {
            JOptionPane.showMessageDialog(null, "off line");
        }

    }

    @Override
    public boolean startConv(ClientInt me, String mail) throws RemoteException {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

      @Override
    public Hashtable getFriends(String eMail) throws RemoteException {
        Hashtable<String, String> friends = new Hashtable();
        ResultSet temp;
        PreparedStatement ps5;
        try {
            ps5 = c.prepareStatement("select contact,nickname from user u,contact_list c where owner=? and email=contact and c.status='friend'");
            ps5.setString(1, eMail);
            rs = ps5.executeQuery();
            while (rs.next()) {
                String userMail = rs.getString("contact");
                String userNick = rs.getString("nickname");
                friends.put(userMail, userNick);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return friends;
    }

    @Override
    public void addfriendtogroupchat(String fr) throws RemoteException {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void sendMessageTogroup(Vector<String> group, String chatname, String msg) throws RemoteException {
        ClientInt ch;
        for (String mail : group) {
            if (online.containsKey(mail)) {
                ch = online.get(mail);
                ch.startNewGroupChat(chatname, mail, this);
                ch.sendGMessageToClient(chatname, msg);
                ch.resetGroup(chatname, group);
                ch.repaintGroupChat(chatname, group);
            }

        }
    }

    @Override
    public void sendGroup(String chatname, Vector<String> g) throws RemoteException {
        ClientInt ch;
        for (String mail : g) {
            ch = online.get(mail);
            ch.resetGroup(chatname, g);
            ch.repaintGroupChat(chatname, g);
        }

    }

    public void start() throws Exception {
        try {
            r = LocateRegistry.createRegistry(7777);
            r.rebind("ChatServer", this);
            System.out.println("RMI initialized...");
        } catch (Exception ex) {
            System.out.println("Start server method exception");
            ex.printStackTrace();
        }
    }

    public void stop() throws Exception {
        //MUST NOTIFY CLIENTS HERE

        r.unbind("ChatServer");
        unexportObject(this, true);
        unexportObject(r, true);
        System.out.println("Server stopped");
    }

    public void serverGUIControl(ServerGUI gui) {
        this.gui = gui;
    }

    public void sendBroadcast(String msg) {
        Enumeration<ClientInt> all = online.elements();
        while (all.hasMoreElements()) {
            try {

                all.nextElement().sendMessage(msg);
            } catch (RemoteException ex) {
                JOptionPane.showMessageDialog(null, "error in sending");

                Logger.getLogger(ServerImp.class.getName()).log(Level.SEVERE, null, ex);
                continue;
            }
        }
    }

    public void serverDown() {
        Enumeration<ClientInt> all = online.elements();
        while (all.hasMoreElements()) {
            try {

                all.nextElement().down();
            } catch (RemoteException ex) {
               
               Logger.getLogger(ServerImp.class.getName()).log(Level.SEVERE, null, ex);

            }
        }
    }

    
    @Override
  public int addFriend(final String myMail, final String Fmail, final String message) throws RemoteException{
      System.out.println("test"+myMail+Fmail+message);
      if(myMail.equals(Fmail))
      {
        System.out.println("You can't add yourself!");
        return 1;
      }
      
    try{
      ps4=c.prepareStatement("select * from user where email=?");
      ps4.setString(1, Fmail);
      rs=ps4.executeQuery();
      
      if(rs.next())
      {
        ps4=c.prepareStatement("select owner, contact,status from contact_list where owner=? and contact=?");
        ps4.setString(1, myMail);
        ps4.setString(2, Fmail);
        rs=ps4.executeQuery();
        if(rs.next()){
          System.out.println("User is already a contact");
          return 2;
        }
      }
      else
      {
        System.out.println("User does not exist!");
        return 3;
      }
    }
    catch(Exception ex){
      System.out.println("Exception in addFriend");
      ex.printStackTrace();
    }
    try{
      ps4=c.prepareStatement("insert into contact_list values (?,?,?,?)");
      ps4.setString(1,myMail);
      ps4.setString(2,Fmail);
      ps4.setString(3,"friend");
      ps4.setString(4,message);
      ps4.executeUpdate();
      ps4=c.prepareStatement("insert into contact_list values (?,?,?,?)");
      ps4.setString(1, Fmail);
      ps4.setString(2, myMail);
      ps4.setString(3, "pending");
      ps4.setString(4,"");
      ps4.executeUpdate();
      if(online.containsKey(Fmail)){
          new Thread()
          {
            public void run() {
                try {
                    online.get(Fmail).showRequest(myMail, Fmail, message, ServerImp.this);
                } catch (RemoteException ex) {
                    Logger.getLogger(ServerImp.class.getName()).log(Level.SEVERE, null, ex);
                }
                this.stop();
            }
          }.start();
      }
      return 4;
    }
    catch(Exception ex){
      ex.printStackTrace();
    }
      return 5;
  }

    @Override
    public void confirmRequest(String myMail, String fMail, int choice) throws RemoteException {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void showRequests(String mail) throws RemoteException {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
    @Override
    public Hashtable onlineFriends(String email) throws RemoteException{
      Hashtable<String, String> friends;
      Hashtable<String, String> onlineFriend = new Hashtable();
      try {
          friends = getFriends(email);
            Enumeration<String> e = friends.keys();
            while (e.hasMoreElements()){
                Object o = e.nextElement();
                if(online.containsKey(o.toString())){
                    onlineFriend.put(o.toString(),status.get(o.toString()).toString());
                }
            }
            return onlineFriend;
      } catch (RemoteException ex) {
          Logger.getLogger(ServerImp.class.getName()).log(Level.SEVERE, null, ex);
      }
        return null;
        
    }
    

}
